package org.quickjsf.common.util;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import javax.el.ValueExpression;
import javax.faces.application.Application;
import javax.faces.bean.ManagedBean;
import javax.faces.component.UIComponent;
import javax.faces.component.UINamingContainer;
import javax.faces.context.FacesContext;
import javax.faces.convert.BigDecimalConverter;
import javax.faces.convert.BigIntegerConverter;
import javax.faces.convert.BooleanConverter;
import javax.faces.convert.ByteConverter;
import javax.faces.convert.CharacterConverter;
import javax.faces.convert.Converter;
import javax.faces.convert.DateTimeConverter;
import javax.faces.convert.DoubleConverter;
import javax.faces.convert.FloatConverter;
import javax.faces.convert.IntegerConverter;
import javax.faces.convert.LongConverter;
import javax.faces.convert.NumberConverter;
import javax.faces.convert.ShortConverter;
import javax.faces.el.ValueBinding;
import javax.faces.event.ActionEvent;
import javax.faces.event.MethodExpressionActionListener;
import javax.inject.Named;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Component;

/**
* @author tenyo
*
*
*/
public class FacesUtil {
    private static final Log log = LogFactory.getLog(FacesUtil.class);
    private static final String DEFAULT_BUNDLE = "javax.faces.Messages";
    
    /**
     * Get servlet context.
     *
     * @return the servlet context
     */
    public static ServletContext getServletContext() {
        return getServletContext(FacesContext.getCurrentInstance());
    }
    
    /**
     * Get servlet context.
     * @param facesContext FacesContext
     * @return the servlet context
     */
    public static ServletContext getServletContext(FacesContext facesContext) {
        return (ServletContext)facesContext.getExternalContext().getContext();
    }
    
    /**
     * Get managed bean based on the bean name.
     *
     * @param beanName the bean name
     * @return the managed bean associated with the bean name
     */
    public static Object getManagedBean(String beanName) {
        return getManagedBean(FacesContext.getCurrentInstance(), beanName);
    }
    
    /**
     * Get managed bean based on the bean name.
     * @param facesContext FacesContext
     * @param beanName the bean name
     * @return the managed bean associated with the bean name
     */
    public static Object getManagedBean(FacesContext facesContext, String beanName) {
        Object o = getValueBinding(getJsfEl(beanName)).getValue(facesContext);
        
        return o;
    }
    
    /**
     * Remove the managed bean based on the bean name.
     *
     * @param beanName the bean name of the managed bean to be removed
     */
    public static void resetManagedBean(String beanName) {
        resetManagedBean(FacesContext.getCurrentInstance(), beanName);
    }
    
    /**
     * Remove the managed bean based on the bean name.
     *
     * @param facesContext FacesContext
     * @param beanName the bean name of the managed bean to be removed
     */
    public static void resetManagedBean(FacesContext facesContext, String beanName) {
        getValueBinding(getJsfEl(beanName)).setValue(facesContext, null);
    }
    
    /**
     * Store the managed bean inside the session scope.
     *
     * @param beanName the name of the managed bean to be stored
     * @param managedBean the managed bean to be stored
     */
    public static void setManagedBeanInSession(String beanName, Object managedBean) {
        setManagedBeanInSession(FacesContext.getCurrentInstance(), beanName, managedBean);
    }
    
    public static void setObjectInSession(String key, Object value) {
    	FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(key, value);
    }
    
    public static Object getObjectInSession(String key) {
    	return FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(key);
    }
    
    public static Object removeObjectInSession(String key) {
    	return FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove(key);
    }
    
    /**
     * Store the managed bean inside the session scope.
     *
     * @param beanName the name of the managed bean to be stored
     * @param managedBean the managed bean to be stored
     */
    public static void setManagedBeanInSession(FacesContext facesContext,
            String beanName, Object managedBean) {
        facesContext.getExternalContext().getSessionMap().put(beanName, managedBean);
    }
    
    /**
     * Get parameter value from request scope.
     *
     * @param name the name of the parameter
     * @return the parameter value
     */
    public static String getRequestParameter(String name) {
        return getRequestParameter(FacesContext.getCurrentInstance(), name);
    }
    
    /**
     * Get parameter value from request scope.
     *
     * @param name the name of the parameter
     * @return the parameter value
     */
    public static String getRequestParameter(FacesContext facesContext, String name) {
        return (String)facesContext.getExternalContext().getRequestParameterMap().get(name);
    }
        
    /**
     *
     * @param locale
     * @return
     */
    public static ResourceBundle getApplicationBundle(Locale locale) {
        return getApplicationBundle(FacesContext.getCurrentInstance(), locale);
    }
    
    /**
     * get application bundle
     * @param facesContext
     * @param locale
     * @return
     */
    public static ResourceBundle getApplicationBundle(FacesContext facesContext, Locale locale) {
        String bundleName = facesContext.getApplication().getMessageBundle();
        if (bundleName != null) {
            try{
                return ResourceBundle.getBundle( bundleName, locale,
                        Thread.currentThread().getContextClassLoader() );
                
            } catch (MissingResourceException e) {
                log.error("Resource bundle " + bundleName + " could not be found.");
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * get default bundle
     *
     * @param locale
     * @return
     */
    public static ResourceBundle getDefaultBundle(Locale locale) {
        try {
            return ResourceBundle.getBundle(DEFAULT_BUNDLE, locale,
                    FacesContext.class.getClassLoader());
            
        } catch (MissingResourceException e) {
            log.error("Resource bundle " + DEFAULT_BUNDLE + " could not be found.");
            return null;
        }
    }
    
    /**
     * get current instance of Application
     *
     * @return current instance of Application
     */
    public static Application getApplication() {
        return FacesContext.getCurrentInstance().getApplication();
    }
    
    public static Locale  getLocale() {
        return FacesContext.getCurrentInstance().getViewRoot().getLocale();
    }
    
    /**
     * get ValueBinding by an EL expression which like "#{expression}"
     * @param el
     * @return
     */
    public static ValueBinding getValueBinding(String el) {
        return getApplication().createValueBinding(el);
    }
    
    /**
     * get the instanceof of HttpServletRequest
     * @return
     */
    public static HttpServletRequest getServletRequest() {
        return (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
    }
    
    private static Object getElValue(String el) {
        return getValueBinding(el).getValue(FacesContext.getCurrentInstance());
    }
    
    private static String getJsfEl(String value) {
        return "#{" + value + "}";
    }
    
    public static ValueExpression createValueExpression(String el) {
		FacesContext context = FacesContext.getCurrentInstance();
		return getApplication().getExpressionFactory()
				.createValueExpression(context.getELContext(), el, Object.class);
	}
    
    public static ValueExpression createObjValueExpression(String objEl) {
		FacesContext context = FacesContext.getCurrentInstance();
		return getApplication().getExpressionFactory()
				.createValueExpression(context.getELContext(), "#{"+objEl+"}", Object.class);
	}
    
    public static MethodExpressionActionListener createActionListener(String actionListenerExpression) {
        FacesContext context = FacesContext.getCurrentInstance();
        Class [] classList=new Class[1];
        classList[0]=ActionEvent.class;
        return new MethodExpressionActionListener(context.getApplication().getExpressionFactory()
            .createMethodExpression(context.getELContext(), actionListenerExpression, null, classList));
    }
    
    public static UIComponent getUIComponent(String clientId) {
		return FacesContext.getCurrentInstance().getViewRoot().findComponent(clientId);
    }
    
    public static String getSeparatorChar() {
        FacesContext ctx = FacesContext.getCurrentInstance();
        return String.valueOf(UINamingContainer.getSeparatorChar(ctx));
    }
    
    public static String getManagedBeanName(Class<?> managedBeanCls) {
		String manageBeanName = null;
		if (managedBeanCls.getAnnotation(Component.class) != null) {
			manageBeanName = managedBeanCls.getAnnotation(Component.class).value();
		} else if (managedBeanCls.getAnnotation(Named.class) != null) {
			manageBeanName = managedBeanCls.getAnnotation(Named.class).value();
		} else if (managedBeanCls.getAnnotation(ManagedBean.class) != null) {
			manageBeanName = managedBeanCls.getAnnotation(ManagedBean.class).name();
		} else {
			throw new NullPointerException("manageBean name no found!");
		}
		return manageBeanName;
	}
	
	public static Converter getFacesConverterByType(Class<?> returnType) {
		if (returnType == null) {
			return null;
			
		} else if (returnType.equals(Byte.class)) {
			return new ByteConverter();
			
		} else if (returnType.equals(Short.class)) {
			return new ShortConverter();
			
		} else if (returnType.equals(Integer.class)) {
			return new IntegerConverter();
			
		} else if (returnType.equals(Long.class)) {
			return new LongConverter();
			
		} else if (returnType.equals(Float.class)) {
			return new FloatConverter();
			
		} else if (returnType.equals(Double.class)) {
			return new DoubleConverter();
			
		} else if (returnType.equals(BigDecimal.class)) {
			return new BigDecimalConverter();
			
		} else if (returnType.equals(Character.class)) {
			return new CharacterConverter();
		} else if (returnType.equals(Float.class)) {
			return new FloatConverter();
			
		} else if (returnType.equals(Boolean.class)) {
			return new BooleanConverter();
			
		} else if (returnType.equals(Date.class)) {
			DateTimeConverter dateTimeConverter = new DateTimeConverter();
			dateTimeConverter.setPattern("yyyy-MM-dd HH:mm:ss");
			return dateTimeConverter;
			
		} else if (returnType.equals(BigInteger.class)) {
			return new BigIntegerConverter();
			
		} else if (returnType.equals(Number.class)) {
			return new NumberConverter();
			
		}
		
		return null;
	}
}
